package matchthreegame.utils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;

import matchthreegame.extensions.MatchThreeGameZoneExtension;
import matchthreegame.gameLogic.GameSwap;

public class BotsManager {
	
	public WeakReference<MatchThreeGameZoneExtension> extension;
	private static BotsManager instance = null;
	private ArrayList<Bot> allBots = new ArrayList<Bot>();
	private static Random random = new Random();
 	private static ArrayList<String> botNames = new ArrayList<String>(Arrays.asList(
//			"Young Boss",
//			"Down_4_U",
//			"TheLivingOne",
//			"King_Robstro",
//			"SiLvEr SurFeR",
//			"Curling Iron",
//			"Tomomi_Kyou",
//			"bog-bog",
//			"Daichi",
//			"Leodane",
//			"DURDICA",
//			"The_Brickwall",
//			"Pan-Head",
//			"FIONNGHUALA",
//			"BadGirlRose",
//			"Dari",
//			"Angela",
//			"Persian Plus",
//			"double d",
//			"Mari-Cruzz",
//			"*KillerBoy*",
//			"Gaiana",
//			"UnBEATen",
//			"Caiaphas",
//			"Hanyariel",
			"Glognus",
			"Alexios_Dzaha",
			"Freppi",
			"Mel-krene",
			"sweet_poison",
			"Agapito",
			"Bunar",
			"Corbi_Nian",
			"Erlwin",
			"FriedeMann",
			"EnErGizEr",
			"BeNvEnUtO",
			"Ischabax",
			"Angrboda",
			"Stalk3r",
			"Mog",
			"Quoky",
			"goten",
			"AlieNatioN",
			"yuzuki",
			"Luy",
			"Vul’Zabal",
			"say what",
			"gallchobhar",
			"Aintzane",
			"cuteandsweet",
			"Sweet Candy",
			"Xeeshan",
			"Mia Genoveffa",
			"JEbUS",
			"Pooh Bear",
			"Dark Places",
			"hOOligan",
			"lolsmurf",
			"BloodyMary",
			"Agostinho",
			"fryderyk",
			"lucky1_s",
			"HoT FeVeR",
			"Morbet",
			"'Ammar",
			"SHINJU",
			"Morena",
			"LIl' MEXICAN",
			"Pekahe",
			"_Im_A_Rebel_",
			"Hoobaskank",
			"wuuut",
			"east_princess",
			"Malar",
			"RUARAIDH",
			"Brielle",
			"aldona_makena",
			"Ashe",
			"Eanraig-Behan",
			"Funky Monkey",
			"Remember",
			"Na’ima",
			"'EFRAYIM",
			"Uukra",
			"gecko",
			"Door_Locked",
			"Datn",
			"Kamikaze",
			"Hirokuo",
			"Hakon",
			"Clannia",
			"rabab",
			"Xerane",
			"Eduárdo",
			"Elda Hanim",
			"p!zzàmàn",
			"CheChe",
			"Gilaras",
			"KEN’ICHI",
			"Burcu",
			"Hulka",
			"rosa_bina",
			"Ruli",
			"Frobo",
			"Allessa",
			"Quokon",
			"Imad_Jamil",
			"Argentaha",
			"Kun-hag",
			"Morel",
			"short funk'",
			"Deniz Parah",
			"Winnie_11",
			"Xolgima",
			"s.i.l.e.n.t.",
			"Debora",
			"Delta_Mona",
			"Maelirin",
			"Essrait",
			"eckehard",
			"Celissan",
			"COrkey",
			"Fantabulous",
			"BeRtHolD",
			"Derbek",
			"Sebezon",
			"Knudze",
			"B-Diddy",
			"Olimpi_dzira",
			"HIKARU",
			"ABD AL-HAMID",
			"Roxess",
			"Sandjo",
			"DURO",
			"Geikara",
			"ruh_ruba",
			"albertina",
			"ÆTHELWEARD",
			"jeremias",
			"Tekrajass",
			"Jahar Dimas",
			"Ziata_Jile",
			"Tonino",
			"Ambrogio",
			"ven-Jol",
			"Cella",
			"Ordina",
			"Ermenegildo",
			"Doruk",
			"candela-can",
			"Chiko",
			"Pharoh-mohad",
			"Xenia",
			"Eikalen",
			"Urakaal",
			"Emeliaa",
			"toljoy",
			"suzana93",
			"Baiturkone",
			"Marnah",
			"Enioni",
			"Gelador",
			"Delinos",
			"Doreshana",
			"Shakti-Bar",
			"Yuanna",
			"Riko",
			"sabeen",
			"Nahar",
			"Antonio",
			"Nikakris",
			"GOROU",
			"Calliope",
			"Red-3cLiPs",
			"longbow",
			"saw",
			"SNIFFY",
			"* Holly *",
			"DaRk_SiDe",
			"eclipse",
			"_Viper_",
			"Evil King",
			"Desperado",
			"IronDragon",
			"sweet lips",
			"foxy red",
			"Juicy",
			"S u Z u K i",
			"wazzap",
			"G-6",
			"wiper",
			"persian tiger",
			"krazy kat",
			"cat fish",
			"sugar pot",
			"Romulus ",
			"*ONLY*",
			"Knight Justin",
			"Snakeman"
			));
 	
 	private static ArrayList<String> hardBotNames = new ArrayList<String>(Arrays.asList(
			"Young Boss",
			"Down_4_U",
			"TheLivingOne",
			"King_Robstro",
			"SiLvEr SurFeR",
			"Curling Iron",
			"Tomomi_Kyou",
			"bog-bog",
			"Daichi",
			"Leodane",
			"DURDICA",
			"The_Brickwall",
			"Pan-Head",
			"FIONNGHUALA",
			"BadGirlRose",
			"Dari",
			"Angela",
			"Persian Plus",
			"double d",
			"Mari-Cruzz",
			"*KillerBoy*",
			"Gaiana",
			"UnBEATen",
			"Caiaphas",
			"Hanyariel"
 			));

	public static BotsManager getBotsManager() {
		
        if ( instance == null ) {
            instance = new BotsManager();
        }
        
        return instance;
        
    }
	
	private BotsManager() {
		
		initBots();
		
	}
	
	public Bot getBot() {
		
		int random = BotsManager.random.nextInt(BotsManager.botNames.size() - 1) + 1;
		
		return allBots.get(random);
		
	}
	
	public GameSwap getBotMoveFor(HashMap<Integer, Integer> neededCookies, HashSet<GameSwap> possibleSwaps, 
			int botHeroPower, int type5Counter, int type6Counter, int botLevel) {
		
		//Check for 5 rank chains
		ArrayList<GameSwap> rank5Swaps = getAllSwapsThatAre5Rank(possibleSwaps);
		if ( rank5Swaps.size() > 0)  {
			extension.get().trace("$$$ 5 rank chain");
			return rank5Swaps.get(0);
		}
		
		//Check for one move win. If need only <= 3 cookies from one kind and in posible swaps there is that kind of swap
		if ( checkIfNeedsOnly3OrLessFromOneKind(neededCookies, possibleSwaps) ) {
			HashSet<GameSwap> swapsThatContainsNeededCookieTypes = getAllSwapsThatContainNeededType(neededCookies, possibleSwaps);
			GameSwap botMove = null;
			for (GameSwap swap : swapsThatContainsNeededCookieTypes) {
				botMove = swap;
				break;
			}
			if ( botMove != null ) {
				extension.get().trace("$$$ One move win");
				return botMove;
			}
		}
		
		//Depending on hero level check if should make random move
		if ( shouldMakeRandomMoveForBotLevel(botLevel) ) {
			GameSwap botMove = null;
			for (GameSwap swap : possibleSwaps) {
				botMove = swap;
				extension.get().trace("$$$ Random bot move depending because of hero level");
				break;
			}
			return botMove;
		}

		//Check if should search for swap for hero skills
		if ( checkIfShouldGetSwapForHeroSkills(botHeroPower) ) {
			GameSwap botMove = getSwapForHeroSkills(possibleSwaps, type5Counter, type6Counter);
			if ( botMove != null ) {
				extension.get().trace("$$$ Hero swap move on intention");
				return botMove;
			}
		}

		//Check for 4 rank chains
		ArrayList<GameSwap> rank4Swaps = getAllSwapsThatAre4Rank(possibleSwaps);
		if ( rank4Swaps.size() > 0) {
			extension.get().trace("$$$ 4 rank chain");
			return rank4Swaps.get(0);
		}
		
		//Get good swaps
		HashSet<GameSwap> swapsThatContainsNeededCookieTypes = getAllSwapsThatContainNeededType(neededCookies, possibleSwaps);
		
		//Check if could get swap in the bottom of the board
		GameSwap botMove = getBottomGameSwap(swapsThatContainsNeededCookieTypes);
		if ( botMove != null ) {
			extension.get().trace("$$$ Bottom move");
			return botMove;
		}

		//Try to get swap for hero skills
		botMove = getSwapForHeroSkills(possibleSwaps, type5Counter, type6Counter);
		if ( botMove != null ) {
			extension.get().trace("$$$ Hero swap move just before random move");
			return botMove;
		}
		
		//If there is no good move make some random move
		if ( botMove == null ) {
			for (GameSwap swap : possibleSwaps) {
				botMove = swap;
				extension.get().trace("$$$ Random bot move");
				break;
			}
		}
		
		return botMove;
		
	}
	
	
	private boolean shouldMakeRandomMoveForBotLevel(int botLevel) {
		
		boolean shouldMakeRandomMove = false;
		
		int random = BotsManager.random.nextInt(100) + 1;
		
		switch (botLevel) {
		case 0:
			shouldMakeRandomMove = false;
			break;
		case 1:
			if ( random < 10 ) shouldMakeRandomMove = true;
			break;
		case 2:
			if ( random < 20 ) shouldMakeRandomMove = true;
			break;
		case 3:
			if ( random < 30 ) shouldMakeRandomMove = true;
			break;
		case 4:
			if ( random < 40 ) shouldMakeRandomMove = true;
			break;
		case 5:
			if ( random < 50 ) shouldMakeRandomMove = true;
			break;

		default:
			break;
		}
		
		return shouldMakeRandomMove;
		
	}
	
	private GameSwap getSwapForHeroSkills(HashSet<GameSwap> possibleSwaps, int type5Counter, int type6Counter) {
		
		int typeToSearch = type5Counter > type6Counter ? 6 : 5;
		
		//First try to get the desired type
		ArrayList<GameSwap> desiredCookieTypeArray = new ArrayList<GameSwap>();
		for (GameSwap gameSwap : possibleSwaps) {
			if ( gameSwap.containsCookieType(typeToSearch) ) {
				desiredCookieTypeArray.add(gameSwap);
			}
		}
		
		//If no game swaps containig chains from the desired type, get the other type used by hero
		if ( desiredCookieTypeArray.size() == 0 ) {
			typeToSearch = typeToSearch == 5 ? 6 : 5;
			
			for (GameSwap gameSwap : possibleSwaps) {
				if ( gameSwap.containsCookieType(typeToSearch) ) {
					desiredCookieTypeArray.add(gameSwap);
				}
			}
			
		}
		
		return desiredCookieTypeArray.size() > 0 ? desiredCookieTypeArray.get(0) : null ;
		
	}

	private boolean checkIfShouldGetSwapForHeroSkills(int botHeroPower) {
		
		boolean shouldGetSwapForHero = false;
		int random = BotsManager.random.nextInt(100) + 1;
		
		switch (botHeroPower) {
		case 1:
			if ( random < 5 ) shouldGetSwapForHero = true;
			break;
		case 2:
			if ( random < 15 ) shouldGetSwapForHero = true;
			break;
		case 3:
			if ( random < 25 ) shouldGetSwapForHero = true;
			break;
		case 4:
			if ( random < 35 ) shouldGetSwapForHero = true;
			break;
		case 5:
			if ( random < 50 ) shouldGetSwapForHero = true;
			break;
		case 6:
			if ( random < 75 ) shouldGetSwapForHero = true;
			break;
		case 7:
			if ( random < 85 ) shouldGetSwapForHero = true;
			break;
//		case 8:
//			if ( random < 75 ) shouldGetSwapForHero = true;
//			break;
//		case 9:
//			if ( random < 85 ) shouldGetSwapForHero = true;
//			break;

		default:
			break;
		}
		
		return shouldGetSwapForHero;
		
	}
	
	
	private GameSwap getBottomGameSwap(HashSet<GameSwap> swapsThatContainsNeededCookieTypes) {
		
		GameSwap bottomSwap = null;
		int lowestRowRank = 10;
		
		for (GameSwap swap : swapsThatContainsNeededCookieTypes) {
			if ( swap.getRowRank() < lowestRowRank ) {
				lowestRowRank = swap.getRowRank();
				bottomSwap = swap;
			}
		}
		
		return bottomSwap;
		
	}
	
	private boolean checkIfNeedsOnly3OrLessFromOneKind(HashMap<Integer, Integer> neededCookies, HashSet<GameSwap> possibleSwaps) {
		
		boolean needsOnly3OrLessFromOneKind = false;
		int cookiesTypesNeeded = 0;
		
		for (Integer type : neededCookies.keySet()) {
			if ( neededCookies.get(type) > 0 ) {
				++cookiesTypesNeeded;
				if ( cookiesTypesNeeded > 1 ) return false;
				
				if ( neededCookies.get(type) <= 3 ) {
					needsOnly3OrLessFromOneKind = true;
				}
				
			}
		}
		
		if ( needsOnly3OrLessFromOneKind ) {
			HashSet<GameSwap> swapsThatContainsNeededCookieTypes = getAllSwapsThatContainNeededType(neededCookies, possibleSwaps);
			if ( swapsThatContainsNeededCookieTypes.size() == 0 ) return false;
		}
		
		return needsOnly3OrLessFromOneKind;
		
	}
	
	//Returns 4 rank swaps if there are any
	private ArrayList<GameSwap> getAllSwapsThatAre4Rank(HashSet<GameSwap> possibleSwaps) {
		
		ArrayList<GameSwap> highRankSwaps = new ArrayList<GameSwap>();
		
		for (GameSwap gameSwap : possibleSwaps) {
			if ( gameSwap.getChainRank() == 4 ) {
				highRankSwaps.add(gameSwap);
			}
		}
		
		return highRankSwaps;
		
	}
	
	//Returns 5 rank swaps if there are any
	private ArrayList<GameSwap> getAllSwapsThatAre5Rank(HashSet<GameSwap> possibleSwaps) {
		
		ArrayList<GameSwap> highRankSwaps = new ArrayList<GameSwap>();
		
		for (GameSwap gameSwap : possibleSwaps) {
			if ( gameSwap.getChainRank() == 5 ) {
				highRankSwaps.add(gameSwap);
			}
		}
		
		return highRankSwaps;
		
	}

	//Returns only swaps containing types that are needed for win
	private HashSet<GameSwap> getAllSwapsThatContainNeededType(HashMap<Integer, Integer> neededCookies, HashSet<GameSwap> possibleSwaps) {
		
		HashSet<GameSwap> goodSwaps = new HashSet<GameSwap>();
		
		for (Integer type : neededCookies.keySet()) {
			if ( neededCookies.get(type) > 0 ) {
				
				//There is need for this type of cookie to win
				for (GameSwap gameSwap : possibleSwaps) {
					if ( gameSwap.containsCookieType(type) ) {
						goodSwaps.add(gameSwap);
					}
				}
				
			}
		}
		
		return goodSwaps;
		
	}
	
	public Bot getBotByName(String botName) {
		
		Bot botToReturn = null;
		
		for (Bot bot : allBots) {
			if ( botName.equals(bot.getBotName()) ) {
				botToReturn = bot;
				break;
			}
		}
		
		return botToReturn;
		
	}
	
	//Create bots
	private void initBots() {
		
		for (int i = 0; i < BotsManager.botNames.size(); i++) {
			
			String botName = BotsManager.botNames.get(i);
			Bot bot = new Bot(botName);
			allBots.add(bot);
			
		}
		
	}

}
